﻿using Extended.WPF.Core.Helpers;
using Extented.UI.Core.Native;
using System;
using System.Collections.Generic;
using Extented.UI.Core.Utils;
using System.Linq;
using System.Text;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;

namespace System.Windows.Controls
{
    public static class BorderExtension
    {
        public static readonly DependencyProperty ClipChildProperty =
            DependencyProperty.RegisterAttached("ClipChild", typeof(bool), typeof(BorderExtension), new PropertyMetadata(OnClipChildChanged));
        public static bool GetClipChild(Border border)
        {
            return (bool)border.GetValue(ClipChildProperty);
        }
        public static void SetClipChild(Border border, bool value)
        {
            border.SetValue(ClipChildProperty, value);
        }
        public static void ClipChild(this Border border)
        {
            if (border.Child != null)
                border.Child.Clip = border.GetChildClip();
        }
        public static Geometry GetChildClip(this Border border)
        {
            if (border.Child == null || !border.IsInVisualTree())
                return null;
            var rect = RectHelper.New(border.GetSize());
            RectHelper.Deflate(ref rect, border.BorderThickness);
            if (border.Child.GetVisible())
                rect = border.MapRect(rect, (FrameworkElement)border.Child);
            else
                rect.X = rect.Y = 0;
            var cornerRadius = border.CornerRadius;
            var borderThickness = border.BorderThickness;
            var corner = new Size[]
			{
				new Size(Math.Max(0, cornerRadius.TopLeft - borderThickness.Left / 2), Math.Max(0, cornerRadius.TopLeft - borderThickness.Top / 2)),
				new Size(Math.Max(0, cornerRadius.TopRight - borderThickness.Right / 2), Math.Max(0, cornerRadius.TopRight - borderThickness.Top / 2)),
				new Size(Math.Max(0, cornerRadius.BottomRight - borderThickness.Right / 2), Math.Max(0, cornerRadius.BottomRight - borderThickness.Bottom / 2)),
				new Size(Math.Max(0, cornerRadius.BottomLeft - borderThickness.Left / 2), Math.Max(0, cornerRadius.BottomLeft - borderThickness.Bottom / 2))
			};
            var figure = new PathFigure { IsClosed = true };
            figure.StartPoint = new Point(rect.Left, rect.Top + corner[0].Height);
            figure.Segments.Add(new ArcSegment
            {
                Point = new Point(rect.Left + corner[0].Width, rect.Top),
                Size = corner[0],
                SweepDirection = SweepDirection.Clockwise
            });
            figure.Segments.Add(new LineSegment { Point = new Point(rect.Right - corner[1].Width, rect.Top) });
            figure.Segments.Add(new ArcSegment
            {
                Point = new Point(rect.Right, rect.Top + corner[1].Height),
                Size = corner[1],
                SweepDirection = SweepDirection.Clockwise
            });
            figure.Segments.Add(new LineSegment { Point = new Point(rect.Right, rect.Bottom - corner[2].Height) });
            figure.Segments.Add(new ArcSegment
            {
                Point = new Point(rect.Right - corner[2].Width, rect.Bottom),
                Size = corner[2],
                SweepDirection = SweepDirection.Clockwise
            });
            figure.Segments.Add(new LineSegment { Point = new Point(rect.Left + corner[3].Width, rect.Bottom) });
            figure.Segments.Add(new ArcSegment
            {
                Point = new Point(rect.Left, rect.Bottom - corner[3].Height),
                Size = corner[3],
                SweepDirection = SweepDirection.Clockwise
            });
            var result = new PathGeometry();
            result.Figures.Add(figure);
            return result;
        }
        [IgnoreDependencyPropertiesConsistencyChecker]
        private static DependencyProperty BorderThicknessListener =
            DependencyProperty.RegisterAttached("BorderThicknessListener", typeof(Thickness), typeof(BorderExtension),
                new PropertyMetadata(OnBorderPropertyChanged));
        [IgnoreDependencyPropertiesConsistencyChecker]
        private static DependencyProperty CornerRadiusListener =
            DependencyProperty.RegisterAttached("CornerRadiusListener", typeof(CornerRadius), typeof(BorderExtension),
                new PropertyMetadata(OnBorderPropertyChanged));
        private static void OnBorderPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var border = (Border)o;
            if (border.IsInVisualTree())
            {
                if (e.Property == CornerRadiusListener)
                    border.ClipChild();
                else
                {
                    if (border.Child != null)
                    {
                        var childSlot = new Rect(0, 0, border.ActualWidth, border.ActualHeight);
                        RectHelper.Deflate(ref childSlot, border.BorderThickness);
                        RectHelper.Deflate(ref childSlot, border.Padding);
                        if (LayoutInformation.GetLayoutSlot((FrameworkElement)border.Child) == childSlot)
                        {
                            border.ClipChild();
                            return;
                        }
                    }
                    EventHandler onLayoutUpdated = null;
                    onLayoutUpdated = delegate
                    {
                        border.LayoutUpdated -= onLayoutUpdated;
                        border.ClipChild();
                    };
                    border.LayoutUpdated += onLayoutUpdated;
                }
            }
        }
        private static void OnBorderSizeChanged(object sender, SizeChangedEventArgs e)
        {
            ((Border)sender).ClipChild();
        }
        private static void OnClipChildChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var border = o as Border;
            if (border == null)
                return;
            if ((bool)e.NewValue)
            {
                border.SetBinding(BorderThicknessListener, new Binding("BorderThickness") { Source = border });
                border.SetBinding(CornerRadiusListener, new Binding("CornerRadius") { Source = border });
                border.SizeChanged += OnBorderSizeChanged;
            }
            else
            {
                border.ClearValue(BorderThicknessListener);
                border.ClearValue(CornerRadiusListener);
                border.SizeChanged -= OnBorderSizeChanged;
            }
        }
    }
}
